#include <string>
#include <map>
#include <utility>

#include "Collision.hpp"
#include "HashFunction.hpp"

using namespace std;

void advance(string &s) {
	bool change = true;
	for (int i = 7; i >= 0; i--) {
		if (change) {
			s[i]++;
			change = false;
		}
		if (s[i] == 'Z' + 1) {
			s[i] = 'A';
			change = true;
		} else {
			break;
		}
	}
}

string Collision::crackSignatureSerial(const string &text1, const string &text2, int hashlen) {
	string s = "AAAAAAAA";
	
	//get first part of hash tom compare with
	HashValue hash1 = HashFunction::calculateHash(hashlen, text1);
	
	//begin hashing text2
	HashFunction f(hashlen);
	f.hash(text2);
	f.saveState();
	
	//try all combinations
	do {
		//append the current combination and hash
		f.hash(s);
		f.finish();
		HashValue hash2 = f.getHash();
		f.loadState();
		
		//compare the beginning of the hashes
		if (hash1 == hash2) {
			return s;
		}
		
		//and advance to the next configuration
		advance(s);
	} while (s != "AAAAAAAA");
	
	return "";
}

pair<string, string> Collision::crackSignatureBirthday(const string &text1, const string &text2, int hashlen) {
	
	//begin hashing text1
	HashFunction f1(hashlen);
	f1.hash(text1);
	f1.saveState();
	
	//begin hashing text2
	HashFunction f2(hashlen);
	f2.hash(text2);
	f2.saveState();
	
	std::map<HashValue, std::string> map1;
	std::map<HashValue, std::string> map2;
	
	string s1 = "AAAAAAAA";
	string s2 = "AAAAAAAA";
	
	//try all combinations
	do {
		
		//try hashing the first string in 1000 variations
		for (int i = 0; i < 1000; i++) {
			f1.hash(s1);
			f1.finish();
			HashValue hash1 = f1.getHash();
			f1.loadState();
			
			//and lookup if we already produced the same hash for the second one
			map1.insert(std::pair<HashValue, std::string>(hash1, s1));
			map<HashValue, std::string>::iterator it = map2.find(hash1);
			if (it != map2.end()) {
				return pair<string, string>(s1, it->second);
			}
			
			advance(s1);
		}
		
		//try hashing the second string in 1000 variations
		for (int i = 0; i < 1000; i++) {
			f2.hash(s2);
			f2.finish();
			HashValue hash2 = f2.getHash();
			f2.loadState();
			
			//and lookup if we already produced the same hash for the first one
			map2.insert(std::pair<HashValue, std::string>(hash2, s2));
			map<HashValue, std::string>::iterator it = map1.find(hash2);
			if (it != map1.end()) {
				return pair<string, string>(it->second, s2);
			}
			
			advance(s2);
		}
				
		
	} while (s1 != "AAAAAAAA");
	
	return pair<string, string>(s1, s2);
}

